package org.jing.ext.security;

import org.jing.core.lang.JingException;
import org.jing.core.util.StringUtil;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Base64;

/**
 * Description: <br>
 *
 * @author bks <br>
 * @since 2021-03-03 <br>
 */
public class AES {
    @SuppressWarnings("SpellCheckingInspection") public static final String KEY_EXAMPLE_HEX = "0123456789abcdef";

    @SuppressWarnings("SpellCheckingInspection") public static final String IV_EXAMPLE_HEX = "abcdefghijk1mnop";

    @SuppressWarnings("SpellCheckingInspection") public static final String KEY_EXAMPLE_BASE64 = "wxRaTrE+9oiAWfVoTHUPJA==";

    @SuppressWarnings("SpellCheckingInspection") public static final String IV_EXAMPLE_BASE64 = "UGDDdQXwAPAINfH5nbF3Hw==";

    public static String randomBase64KeyOrIV() {
        return Common.getBase64Encoder().encodeToString(Common.randomBytes(16));
    }

    public static byte[] encrypt(byte[] content, byte[] key, byte[] iv) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
            return cipher.doFinal(content);
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t, "Failed to aes encrypt with [key: {}][iv: {}]", key, iv);
        }
    }

    public static String encryptBase64(String content, String key, String iv, Charset charset) {
        return Common.getBase64Encoder().encodeToString(
            encrypt(
                content.getBytes(charset),
                Common.getBase64Decoder().decode(key),
                Common.getBase64Decoder().decode(iv)
            )
        );
    }

    public static String encryptBase64(String content, String key, String iv, String charset) {
        try {
            return encryptBase64(content, key, iv, Charset.forName(StringUtil.ifEmpty(charset, "UTF-8")));
        }
        catch (UnsupportedCharsetException e) {
            throw new JingException(e, "Unsupported charset [" + charset + "]");
        }
    }

    public static String encryptBase64(String content, String key, String iv) {
        return encryptBase64(content, key, iv, "");
    }

    public static String encryptHex(String content, String key, String iv, Charset charset) {
        return Common.parseByte2HexStr(encrypt(content.getBytes(charset), key.getBytes(), iv.getBytes(charset)));
    }

    public static String encryptHex(String content, String key, String iv, String charset) {
        try {
            return encryptHex(content, key, iv, Charset.forName(StringUtil.ifEmpty(charset, "UTF-8")));
        }
        catch (UnsupportedCharsetException e) {
            throw new JingException(e, "Unsupported charset [" + charset + "]");
        }
    }

    public static String encryptHex(String content, String key, String iv) {
        return encryptHex(content, key, iv, "");
    }

    public static byte[] decrypt(byte[] content, byte[] key, byte[] iv) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
            return cipher.doFinal(content);
        }
        catch (Throwable t) {
            throw new JingException(t, "Failed to aes decrypt with [password: {}][iv: {}]", key, iv);
        }
    }

    public static String decryptBase64(String content, String key, String iv, Charset charset) {
        return new String(decrypt(
            Base64.getDecoder().decode(content),
            Base64.getDecoder().decode(key),
            Base64.getDecoder().decode(iv)
        ), charset);
    }

    public static String decryptBase64(String content, String key, String iv, String charset) {
        try {
            return decryptBase64(content, key, iv, Charset.forName(StringUtil.ifEmpty(charset, "UTF-8")));
        } catch (UnsupportedCharsetException e) {
            throw new JingException("Unsupported charset [" + charset + "]", e);
        }
    }

    public static String decryptBase64(String content, String key, String iv) {
        return decryptBase64(content, key, iv, "");
    }

    public static String decryptHex(String content, String key, String iv, Charset charset) {
        return new String(decrypt(Common.parseHexStr2Byte(content), key.getBytes(), iv.getBytes()), charset);
    }

    public static String decryptHex(String content, String key, String iv, String charset) {
        try {
            return decryptHex(content, key, iv, Charset.forName(StringUtil.ifEmpty(charset, "UTF-8")));
        } catch (UnsupportedCharsetException e) {
            throw new JingException("Unsupported charset [" + charset + "]", e);
        }
    }

    public static String decryptHex(String content, String key, String iv) {
        return decryptHex(content, key, iv, "");
    }

    public static void main(String[] args) {
        String content = "hello world";
        {
            String res = AES.encryptHex(content, KEY_EXAMPLE_HEX, IV_EXAMPLE_HEX);
            System.out.println("hex加密后" + res);
            System.out.println("hex解密后" + AES.decryptHex(res, KEY_EXAMPLE_HEX, IV_EXAMPLE_HEX));
        }
        {
            String res = AES.encryptBase64(content, KEY_EXAMPLE_BASE64, IV_EXAMPLE_BASE64);
            System.out.println("base64加密后" + res);
            System.out.println("base64解密后" + AES.decryptBase64(res, KEY_EXAMPLE_BASE64, IV_EXAMPLE_BASE64));
        }
    }
}
